/**
 * Author : Jorge Mejuto Create : 20/05/2009
 */

package net.eyra.negocio.explotacion.produccion;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import net.eyra.negocio.aplicacion.Constantes;
import net.eyra.negocio.dao.ExplotacionDao;
import net.eyra.negocio.dao.EyraDaoException;
import net.eyra.negocio.entidades.explotacion.Lectura;
import net.eyra.negocio.entidades.explotacion.Parque;
import net.eyra.negocio.entidades.explotacion.Tarificador;
import net.eyra.negocio.entidades.gestion.Usuario;
import net.eyra.utiles.Numeros;
import net.eyra.utiles.Periodo;

public class Produccion {

    /**
     * Devuelve una nueva lista de parques con el resumen de producción por meses desde que el parque entró en funcionamiento. El resumen es por
     * tarificador y habrá una entrada en lectura_tm1 por cada mes.
     * 
     * @param parques
     * @return
     * @throws EyraDaoException
     * @throws ParseException
     */
    public List<Parque> ResumenProduccion(List<Parque> parques) throws EyraDaoException, ParseException {
        ExplotacionDao dao = new ExplotacionDao();
        List<Parque> _parques = new ArrayList<Parque>(parques);

        for (Parque parque : _parques) {
            for (Tarificador tarificador : parque.getTarificadores()) {
                List<Object[]> resumen = dao.ResumenProduccionPorMesYAño(tarificador.getId_tarificador());
                List<Lectura> lecturas = new ArrayList<Lectura>();
                for (Object[] meses : resumen) {
                    Lectura lectura = new Lectura();
                    lectura.setPeriodo((Date) meses[0]);
                    lectura.setActiva_exportada((Double) meses[1]);
                    lectura.setActiva_importada((Double) meses[2]);
                    lectura.setReactiva_q1((Double) meses[3]);
                    lectura.setReactiva_q2((Double) meses[4]);
                    lectura.setReactiva_q3((Double) meses[5]);
                    lectura.setReactiva_q4((Double) meses[6]);
                    lecturas.add(lectura);
                }
                tarificador.setLecturas_tm1(lecturas);
            }
        }

        return _parques;
    }

    protected double BonificacionComplementoReactiva(int bonificacion_factor_potencia, double activa_exportada) {
        return (bonificacion_factor_potencia * activa_exportada * Constantes.TARIFA_REACTIVA) / 100;
    }

    protected int BonificacionFactorPotenciaMaxima(int periodo) {
        if (periodo == Constantes.PERIODO_LLANO) return Constantes.MAXIMA_BONIFICACION_LLANO;
        if (periodo == Constantes.PERIODO_PUNTA) return Constantes.MAXIMA_BONIFICACION_PUNTA;
        return Constantes.MAXIMA_BONIFICACION_VALLE;
    }

    private int BonificacionFactorPotenciaSinConsigna(double factor_potencia, int inductiva, int periodo_dia) {
        int[][] bonificacion_inductiva = new int[3][6];
        int[][] bonificacion_capacitiva = new int[3][6];

        bonificacion_inductiva[Constantes.PERIODO_PUNTA][0] = 0;
        bonificacion_inductiva[Constantes.PERIODO_PUNTA][1] = 0;
        bonificacion_inductiva[Constantes.PERIODO_PUNTA][2] = -1;
        bonificacion_inductiva[Constantes.PERIODO_PUNTA][3] = -2;
        bonificacion_inductiva[Constantes.PERIODO_PUNTA][4] = -3;
        bonificacion_inductiva[Constantes.PERIODO_PUNTA][5] = -4;

        bonificacion_inductiva[Constantes.PERIODO_LLANO][0] = 4;
        bonificacion_inductiva[Constantes.PERIODO_LLANO][1] = 2;
        bonificacion_inductiva[Constantes.PERIODO_LLANO][2] = 0;
        bonificacion_inductiva[Constantes.PERIODO_LLANO][3] = 0;
        bonificacion_inductiva[Constantes.PERIODO_LLANO][4] = 0;
        bonificacion_inductiva[Constantes.PERIODO_LLANO][5] = -4;

        bonificacion_inductiva[Constantes.PERIODO_VALLE][0] = 0;
        bonificacion_inductiva[Constantes.PERIODO_VALLE][1] = 0;
        bonificacion_inductiva[Constantes.PERIODO_VALLE][2] = 2;
        bonificacion_inductiva[Constantes.PERIODO_VALLE][3] = 4;
        bonificacion_inductiva[Constantes.PERIODO_VALLE][4] = 6;
        bonificacion_inductiva[Constantes.PERIODO_VALLE][5] = 8;

        bonificacion_capacitiva[Constantes.PERIODO_PUNTA][0] = 0;
        bonificacion_capacitiva[Constantes.PERIODO_PUNTA][1] = 0;
        bonificacion_capacitiva[Constantes.PERIODO_PUNTA][2] = 2;
        bonificacion_capacitiva[Constantes.PERIODO_PUNTA][3] = 4;
        bonificacion_capacitiva[Constantes.PERIODO_PUNTA][4] = 6;
        bonificacion_capacitiva[Constantes.PERIODO_PUNTA][5] = 8;

        bonificacion_capacitiva[Constantes.PERIODO_LLANO][0] = 4;
        bonificacion_capacitiva[Constantes.PERIODO_LLANO][1] = 2;
        bonificacion_capacitiva[Constantes.PERIODO_LLANO][2] = 0;
        bonificacion_capacitiva[Constantes.PERIODO_LLANO][3] = 0;
        bonificacion_capacitiva[Constantes.PERIODO_LLANO][4] = 0;
        bonificacion_capacitiva[Constantes.PERIODO_LLANO][5] = -4;

        bonificacion_capacitiva[Constantes.PERIODO_VALLE][0] = 0;
        bonificacion_capacitiva[Constantes.PERIODO_VALLE][1] = 0;
        bonificacion_capacitiva[Constantes.PERIODO_VALLE][2] = -1;
        bonificacion_capacitiva[Constantes.PERIODO_VALLE][3] = -2;
        bonificacion_capacitiva[Constantes.PERIODO_VALLE][4] = -3;
        bonificacion_capacitiva[Constantes.PERIODO_VALLE][5] = -4;

        if (inductiva == Constantes.NI_INDUCTIVA_NI_CAPACITIVA) return 0;

        double _factor_potencia = Numeros.Redondeo(factor_potencia, 2);

        if (_factor_potencia == 1)
            return (inductiva == Constantes.INDUCTIVA ? bonificacion_inductiva[periodo_dia][0] : bonificacion_capacitiva[periodo_dia][0]);
        if (_factor_potencia < 1 && _factor_potencia >= 0.98)
            return (inductiva == Constantes.INDUCTIVA ? bonificacion_inductiva[periodo_dia][1] : bonificacion_capacitiva[periodo_dia][1]);
        if (_factor_potencia < 0.98 && _factor_potencia >= 0.97)
            return (inductiva == Constantes.INDUCTIVA ? bonificacion_inductiva[periodo_dia][2] : bonificacion_capacitiva[periodo_dia][2]);
        if (_factor_potencia < 0.97 && _factor_potencia >= 0.96)
            return (inductiva == Constantes.INDUCTIVA ? bonificacion_inductiva[periodo_dia][3] : bonificacion_capacitiva[periodo_dia][3]);
        if (_factor_potencia < 0.96 && _factor_potencia >= 0.95)
            return (inductiva == Constantes.INDUCTIVA ? bonificacion_inductiva[periodo_dia][4] : bonificacion_capacitiva[periodo_dia][4]);

        return (inductiva == Constantes.INDUCTIVA ? bonificacion_inductiva[periodo_dia][5] : bonificacion_capacitiva[periodo_dia][5]);
    }

    private int BonificacionFactorPotenciaConConsignaTipo2(double factor_potencia, int inductiva, int periodo_dia) {
        final int MAXIMA_BONIFICACION = 0;
        final int MAXIMA_PENALIZACION = 1;

        int[][] _inductiva = new int[3][2];
        int[][] _capacitiva = new int[3][2];

        _inductiva[Constantes.PERIODO_PUNTA][MAXIMA_BONIFICACION] = 8;
        _inductiva[Constantes.PERIODO_LLANO][MAXIMA_BONIFICACION] = 4;
        _inductiva[Constantes.PERIODO_VALLE][MAXIMA_BONIFICACION] = 8;

        _inductiva[Constantes.PERIODO_PUNTA][MAXIMA_PENALIZACION] = -4;
        _inductiva[Constantes.PERIODO_LLANO][MAXIMA_PENALIZACION] = -4;
        _inductiva[Constantes.PERIODO_VALLE][MAXIMA_PENALIZACION] = -4;

        _capacitiva[Constantes.PERIODO_PUNTA][MAXIMA_BONIFICACION] = 8;
        _capacitiva[Constantes.PERIODO_LLANO][MAXIMA_BONIFICACION] = 4;
        _capacitiva[Constantes.PERIODO_VALLE][MAXIMA_BONIFICACION] = 8;

        _capacitiva[Constantes.PERIODO_PUNTA][MAXIMA_PENALIZACION] = -4;
        _capacitiva[Constantes.PERIODO_LLANO][MAXIMA_PENALIZACION] = -4;
        _capacitiva[Constantes.PERIODO_VALLE][MAXIMA_PENALIZACION] = -4;

        if (Numeros.Redondeo(factor_potencia, 2) > 0.99)
            return (inductiva == Constantes.INDUCTIVA ? _inductiva[periodo_dia][MAXIMA_BONIFICACION] : _capacitiva[periodo_dia][MAXIMA_BONIFICACION]);

        return (inductiva == Constantes.INDUCTIVA ? _inductiva[periodo_dia][MAXIMA_PENALIZACION] : _capacitiva[periodo_dia][MAXIMA_PENALIZACION]);
    }

    private int BonificacionFactorPotenciaConConsignaTipo3(double factor_potencia, int inductiva, int periodo_dia) {
        final int MAXIMA_BONIFICACION = 0;
        final int MAXIMA_PENALIZACION = 1;

        int[][] _inductiva = new int[3][2];
        int[][] _capacitiva = new int[3][2];

        _inductiva[Constantes.PERIODO_PUNTA][MAXIMA_BONIFICACION] = 8;
        _inductiva[Constantes.PERIODO_LLANO][MAXIMA_BONIFICACION] = 4;
        _inductiva[Constantes.PERIODO_VALLE][MAXIMA_BONIFICACION] = 8;

        _inductiva[Constantes.PERIODO_PUNTA][MAXIMA_PENALIZACION] = -4;
        _inductiva[Constantes.PERIODO_LLANO][MAXIMA_PENALIZACION] = -4;
        _inductiva[Constantes.PERIODO_VALLE][MAXIMA_PENALIZACION] = -4;

        _capacitiva[Constantes.PERIODO_PUNTA][MAXIMA_BONIFICACION] = -4;
        _capacitiva[Constantes.PERIODO_LLANO][MAXIMA_BONIFICACION] = -4;
        _capacitiva[Constantes.PERIODO_VALLE][MAXIMA_BONIFICACION] = -4;

        _capacitiva[Constantes.PERIODO_PUNTA][MAXIMA_PENALIZACION] = -4;
        _capacitiva[Constantes.PERIODO_LLANO][MAXIMA_PENALIZACION] = -4;
        _capacitiva[Constantes.PERIODO_VALLE][MAXIMA_PENALIZACION] = -4;

        if (Numeros.Redondeo(factor_potencia, 2) >= 0.98 && Numeros.Redondeo(factor_potencia, 2) <= 0.99)
            return (inductiva == Constantes.INDUCTIVA ? _inductiva[periodo_dia][MAXIMA_BONIFICACION] : _capacitiva[periodo_dia][MAXIMA_BONIFICACION]);

        return (inductiva == Constantes.INDUCTIVA ? _inductiva[periodo_dia][MAXIMA_PENALIZACION] : _capacitiva[periodo_dia][MAXIMA_PENALIZACION]);
    }

    protected int BonificacionFactorPotencia(double factor_potencia, int inductiva, int periodo_dia, int consigna) {
        switch (consigna) {
            case 1: // Sin Consigna
                return BonificacionFactorPotenciaSinConsigna(factor_potencia, inductiva, periodo_dia);
            case 2: // Consigna Redondeo F.P. a 1
                return BonificacionFactorPotenciaConConsignaTipo2(factor_potencia, inductiva, periodo_dia);
            case 3: // Consigna 0.98 <= F.P. <= 0.99 & INDUCTIVO
                return BonificacionFactorPotenciaConConsignaTipo3(factor_potencia, inductiva, periodo_dia);
        }
        return 0;
    }

    protected static double Factor_Potencia(double activa_exportada, double reactiva_q2, double reactiva_q3) {
        if (reactiva_q2 + reactiva_q3 == 0) return 0;
        if (activa_exportada + (reactiva_q2 - reactiva_q3) == 0) return 0;
        return activa_exportada / Math.sqrt(Math.pow(activa_exportada, 2) + Math.pow(reactiva_q3 - reactiva_q2, 2));
    }

    protected static int InductivaCapacitiva(double reactiva_q2, double reactiva_q3, double activa_exportada) {
        if (reactiva_q2 + reactiva_q3 == 0 || activa_exportada == 0) return Constantes.NI_INDUCTIVA_NI_CAPACITIVA;
        double calculo = reactiva_q2 - reactiva_q3;
        if (calculo > 0) return Constantes.INDUCTIVA;
        if (calculo < 0) return Constantes.CAPACITIVA;
        return Constantes.INDUCTIVA;
    }

    protected int PeriodoDia(Date fecha) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(fecha);
        int hora = cal.get(Calendar.HOUR_OF_DAY);
        if (hora == 0) hora = 24;

        boolean verano = EsHorarioVerano(fecha);

        if (hora > 0 && hora <= 8) return Constantes.PERIODO_VALLE;

        if (verano) {
            if ((hora > 8 && hora <= 11) || (hora > 15 && hora <= 24)) return Constantes.PERIODO_LLANO;
            if (hora > 11 && hora <= 15) return Constantes.PERIODO_PUNTA;
        }

        if ((hora > 8 && hora <= 18) || (hora > 22 && hora <= 24)) return Constantes.PERIODO_LLANO;

        return Constantes.PERIODO_PUNTA;
    }

    protected static boolean EsHorarioVerano(Date fecha) {
        SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm");
        Date inicio_verano = null;
        Date inicio_invierno = null;
        try {
            inicio_verano = sdf.parse(Constantes.INICIO_VERANO);
            inicio_invierno = sdf.parse(Constantes.INICIO_INVIERNO);
        } catch (ParseException e) {
        }

        Periodo periodo = new Periodo(inicio_verano, inicio_invierno);

        return periodo.estaEnPeriodo(fecha);
    }

    public List<String[]> FechasUltimaActualizacion(Usuario usuario) throws EyraDaoException {
        ExplotacionDao dao = new ExplotacionDao();
        List<Object[]> fechas_tm1 = dao.RecuperarFechasUltimaActualizacion(true);
        List<Object[]> fechas_tm2 = dao.RecuperarFechasUltimaActualizacion(false);

        List<String[]> ultimas_fechas = new ArrayList<String[]>();

        SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm");

        for (Object[] _fecha : fechas_tm1) {
            int id_tarificador = (Integer) _fecha[0];
            Date fecha = (Date) _fecha[1];
            String str_fecha_2 = "No hay Lecturas";
            Tarificador tarificador = RecuperarTarificador(usuario, id_tarificador);
            if (tarificador != null) {
                for (Object[] _fecha_2 : fechas_tm2) {
                    if (_fecha_2 != null) {
                        int id_tarificador_2 = (Integer) _fecha_2[0];
                        Date fecha_2 = (Date) _fecha_2[1];
                        if (id_tarificador_2 == tarificador.getId_tarificador()) str_fecha_2 = sdf.format(fecha_2);
                    }
                }
                String[] fecha_tmp = { tarificador.getNombre(), sdf.format(fecha), str_fecha_2 };
                ultimas_fechas.add(fecha_tmp);
            }

        }

        return ultimas_fechas;
    }

    private Tarificador RecuperarTarificador(Usuario usuario, int id_tarificador) {
        List<Tarificador> tarificadores = ListaTarificadoresUsuario(usuario);
        for (Tarificador tarificador : tarificadores) {
            if (tarificador.getId_tarificador() == id_tarificador) return tarificador;
        }

        return null;
    }

    private static List<Tarificador> ListaTarificadoresUsuario(Usuario usuario) {

        List<Tarificador> tarificadores = new ArrayList<Tarificador>();

        for (Parque parque : usuario.getGrupo().getParques()) {
            for (Tarificador tarificador : parque.getTarificadores())
                tarificadores.add(tarificador);
        }

        return tarificadores;
    }

}
